package com.mjk.common.web.getway.transfer.cloud;

import cn.hutool.core.exceptions.ExceptionUtil;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.google.common.base.Preconditions;
import com.mjk.common.base.response.BeeResponse;
import com.mjk.common.tools.gson.GsonUtils;
import com.mjk.common.tools.springextend.BeeApplicationListener;
import com.mjk.common.tools.springextend.BeeSpringBean;
import com.mjk.common.util.*;
import com.mjk.common.web.getway.constns.GetWayError;
import com.mjk.common.web.getway.transfer.GetwayTransfer;
import com.mjk.common.web.getway.transfer.cloud.loadbalancer.NacosServerList;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.NoOpPing;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;
import jakarta.servlet.ServletInputStream;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector;
import org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter;
import org.springframework.cloud.netflix.ribbon.apache.RibbonApacheHttpRequest;
import org.springframework.cloud.netflix.ribbon.apache.RibbonApacheHttpResponse;
import org.springframework.cloud.netflix.ribbon.apache.RibbonLoadBalancingHttpClient;
import org.springframework.cloud.netflix.ribbon.support.RibbonCommandContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.env.Environment;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
 * spring cloud 协议转化 http->spring cloud
 * @author mjk
 *
 */

public class CloudGetWayProtocol implements GetwayTransfer, BeeApplicationListener {
	
	private final Map<String,CloudGetwayModel> cloudMap = new ConcurrentHashMap<>();
	
	private final Map<String, RibbonLoadBalancingHttpClient> clientMap = new ConcurrentHashMap<>();
	
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public BeeResponse invoke(Map paramMap) {
	  String version = MapUtils.getString(paramMap, "version");
	  if(ObjectUtils.isEmpty(version)){
		  return GetWayError.CLOUD_VERSION_IS_ERROR.tranceError("");
	  }
	  CloudGetwayModel model = cloudMap.get(version);
	  RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient = clientMap.get(version);
	  if(null == model && isMapping(version)){//如果是v1默认为manager
		  model = cloudMap.get(DEFAULT_VERSION_MAPPING);
		  ribbonLoadBalancingHttpClient = clientMap.get(DEFAULT_VERSION_MAPPING);
	  }
	  if(null==model){
		  return GetWayError.CLOUD_VERSION_IS_ERROR.tranceError("");		  	
	  }
	  
       DiscoveryClient discoveryClient = BeeSpringBean.getBean(DiscoveryClient.class);
       List<ServiceInstance> instances = Objects.requireNonNull(discoveryClient).getInstances(model.getAppName());
       ServiceInstance instance = !instances.isEmpty() ? instances.get(0) : null;
       if (instance == null) {
    	   return GetWayError.CLOUD_VERSION_IS_ERROR.tranceError("");		  	
       }
       //实例化某个具体request的上下文，如果对应到开放平台上，这些信息就是开放某个具体接口时，录入的API信息      
       RibbonCommandContext context = new RibbonCommandContext(model.getAppName(),"post", instance.getUri() + model.getApi(),
     		  Boolean.FALSE,new LinkedMultiValueMap(),
     		  transferMap(paramMap), null, new ArrayList<>(), null);


		StringBuilder result = new StringBuilder();
       try (
			   RibbonApacheHttpResponse response = ribbonLoadBalancingHttpClient.executeWithLoadBalancer(new RibbonApacheHttpRequest(context));
			   BufferedReader br = new BufferedReader(new InputStreamReader(response.getInputStream(), StandardCharsets.UTF_8));){
           //实例化request，对Service请求调用
           //如果服务接口输出json或xml，可以拿到显示
           String line;
           while((line = br.readLine())!=null){
         	  result.append(line);
           }
           return GsonUtils.json2Bean(result.toString(), BeeResponse.class);
       } catch (Exception e) {	
     	  BeeResponse response = new BeeResponse();
     	  response.setCode(-999);
		   response.setMsg(ExceptionUtil.getMessage(e));
       	  return response;
       }
	}	
	
	@Override
	public void doApplicationEvent(ContextRefreshedEvent event) {
		NacosDiscoveryProperties properties = BeeSpringBean.getBean(NacosDiscoveryProperties.class);
		setEnvironment(event.getApplicationContext().getEnvironment(),properties);
	}
	
	public void setEnvironment(Environment environment,NacosDiscoveryProperties properties) {
		String prefix = "bee.cloud.";	
		String dubboName = environment.getProperty(prefix + "names");
		if (dubboName == null) {
			return;
		}
		for (String each : dubboName.split(",")) {			
			Map<String, Object> dubboProps = PropertyUtil.handle(environment, prefix + each, Map.class);
			Preconditions.checkState(!dubboProps.isEmpty(), "Wrong datasource properties!");
			CloudGetwayModel model = new CloudGetwayModel();
			for(Entry<String,Object> entry : dubboProps.entrySet()){				
				ReflectionUtils.setFieldValue(model, entry.getKey(), entry.getValue());
			}
			cloudMap.put(each, model);
			clientMap.put(each, getHttpClient(model,properties));
		}	
	}
	
	@SuppressWarnings("unchecked")
	private RibbonLoadBalancingHttpClient  getHttpClient(CloudGetwayModel model,NacosDiscoveryProperties properties){
		NacosServerList serverList = new NacosServerList(properties);

		IClientConfig clientConfig = DefaultClientConfigImpl.getClientConfigWithDefaultValues();
	    clientConfig.loadDefaultValues();
	    clientConfig.set(CommonClientConfigKey.DeploymentContextBasedVipAddresses,model.getAppName());
	    if(ObjectUtils.isNotEmpty(model.getTimeout())){
	    	  clientConfig.set(CommonClientConfigKey.ConnectTimeout, Integer.valueOf(model.getTimeout()));
	    	  clientConfig.set(CommonClientConfigKey.ReadTimeout, Integer.valueOf(model.getTimeout()));
	    }
		serverList.initWithNiwsConfig(clientConfig);
	    ILoadBalancer loadBalancer = new ZoneAwareLoadBalancer(clientConfig,new ZoneAvoidanceRule(),new NoOpPing(),serverList,new ZonePreferenceServerListFilter());
		RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient = new RibbonLoadBalancingHttpClient(clientConfig, new DefaultServerIntrospector());
		ribbonLoadBalancingHttpClient.setLoadBalancer(loadBalancer);
		return ribbonLoadBalancingHttpClient;
	}
	
	private LinkedMultiValueMap<String,String> transferMap(String gson){
		Map<String,String> map = GsonUtils.gson2Map(gson);
		LinkedMultiValueMap<String, String> result = new LinkedMultiValueMap<>();
		for(Entry<String,String> entry : map.entrySet()){
			result.set(entry.getKey(), entry.getValue());
		}
		return result;
	}
	
	private LinkedMultiValueMap<String,String> transferMap(Map map){		
		LinkedMultiValueMap<String, String> result = new LinkedMultiValueMap<>();
		for(Object key : map.keySet()){			
			result.set(key.toString(), ObjectUtils.isEmpty(map.get(key))?"":map.get(key).toString());
		}		
		return result;
	}
}
